/*
 * Created Jun 16, 2008. 
 */
package com.wgo.preciseLite.server.transform.xml;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.xmlbeans.XmlCalendar;
import org.apache.xmlbeans.XmlOptions;

import com.wgo.bpot.common.transport.exception.RematoException;
import com.wgo.precise.common.domain.Category;
import com.wgo.precise.common.domain.Project;
import com.wgo.precise.common.domain.PropertyDef;
import com.wgo.precise.common.domain.PropertySet;
import com.wgo.precise.common.domain.PropertyValue;
import com.wgo.precise.common.domain.Requirement;
import com.wgo.precise.common.domain.RequirementSystem;
import com.wgo.precise.common.domain.Stakeholder;
import com.wgo.precise.server.preciseData.CategoryType;
import com.wgo.precise.server.preciseData.ProjectType;
import com.wgo.precise.server.preciseData.PropertyDefType;
import com.wgo.precise.server.preciseData.PropertySetType;
import com.wgo.precise.server.preciseData.PropertyValueType;
import com.wgo.precise.server.preciseData.RefType;
import com.wgo.precise.server.preciseData.RequirementSystemDocument;
import com.wgo.precise.server.preciseData.RequirementType;
import com.wgo.precise.server.preciseData.StakeholderType;
import com.wgo.precise.server.service.BpotXmlUtil;
import com.wgo.preciseLite.server.transform.ExportService;

/**
 * @author Petter L. H. Eide
 * @version $Id: ExportPreciseLiteXml.java 554 2008-06-16 13:07:31Z petter.eide $
 */
public class ExportPreciseLiteXml implements ExportService {

	private Map<Long,RequirementType> requirements;

	/* (non-Javadoc)
	 * @see com.wgo.preciseLite.server.transform.ExportService#export(com.wgo.precise.common.domain.RequirementSystem)
	 */
	@Override
	public byte[] export(RequirementSystem requirementSystem) {
		requirements = new HashMap<Long,RequirementType>();
		RequirementSystemDocument requirementSystemDocument = RequirementSystemDocument.Factory.newInstance();
		if (null != requirementSystem) {
			com.wgo.precise.server.preciseData.RequirementSystemDocument.RequirementSystem rsDoc = requirementSystemDocument.addNewRequirementSystem();
			exportStakeholders(requirementSystem.getStakeholders(), rsDoc);			
			exportPropertySets(requirementSystem, rsDoc);
			exportProjects(requirementSystem.getProjects(), rsDoc);
			setupLinkedRequirements(requirementSystem,rsDoc);
		}
//		RequirementSystemDocument requirementSystemDocument = exportPrecise.createDocument((RequirementSystem)getPersistService().findDbRootSystem(RequirementSystem.class)); // TODO: test this
//		BpotXmlUtil.assertValidDoc(requirementSystemDocument, "Invalid document - something persistent data is inconsistent with xml-schema");
		ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
		XmlOptions xmlOption = new XmlOptions();
		xmlOption.setSavePrettyPrint();
		try {
			requirementSystemDocument.save(byteArrayOutputStream,xmlOption);
		} catch (IOException e) {
			throw new RuntimeException("Unable to create XML-document at server.", e);
		}
		return byteArrayOutputStream.toByteArray();
	}

	private void exportPropertySets(RequirementSystem requirementSystem, com.wgo.precise.server.preciseData.RequirementSystemDocument.RequirementSystem rsDoc) {
		for (PropertySet propertySet : requirementSystem.getPropertySets()) {
			PropertySetType propertySetDoc = rsDoc.addNewPropertySet();
			propertySetDoc.setName(str(propertySet.getName()));
			propertySetDoc.setDescription(str(propertySet.getDescription()));
//			if (null != propertySet.getUpdatedDate()) {
//				propertySetDoc.setLastUpdatedDate(new XmlCalendar(propertySet.getUpdatedDate()));
//			}
			for (PropertyDef propertyDef : propertySet.getPropertyDefs()) {
				PropertyDefType propertyDefDoc = propertySetDoc.addNewPropertyDef();
				propertyDefDoc.setName(str(propertyDef.getName()));
				propertyDefDoc.setDescription(str(propertyDef.getDescription()));
//				propertyDefDoc.setDefaultValue(str(propertyDef.getDefaultValue()));
				propertyDefDoc.setTypeClass(propertyDef.getType().name());
			}
		}
	}

	private static String str(String str){
		if (null == str) {
			return "";
		}
		return str.trim();
	}
	
	private void exportStakeholders(Set<Stakeholder> stakeholders, com.wgo.precise.server.preciseData.RequirementSystemDocument.RequirementSystem rsDoc) {
		for (Stakeholder stakeholder : stakeholders) {
			StakeholderType stakeholderDoc = rsDoc.addNewStakeholder();
			stakeholderDoc.setStakeholderId(getStakeholderRef(stakeholder));
			stakeholderDoc.setFirstName(str(stakeholder.getName()));
			stakeholderDoc.setDescription(str(stakeholder.getDescription()));
		}
	}

	private void exportProjects(Set<Project> projects, com.wgo.precise.server.preciseData.RequirementSystemDocument.RequirementSystem rsDoc) {
		for (Project project : projects) {
			ProjectType projDoc = rsDoc.addNewProject();
			exportProject(project, projDoc);
		}
	}

	private void exportProjects(Set<Project> projects, ProjectType projParentDoc) {
		for (Project project : projects) {
			ProjectType projDoc = projParentDoc.addNewProjectChild();
			exportProject(project, projDoc);
		}
	}

	private void exportProject(Project project, ProjectType projDoc) {
		projDoc.setName(str(project.getName()));
		projDoc.setDescription(str(project.getDescription()));
		exportProjects(project.getSubProjects(), projDoc);
		exportCategories(project.getCategories(), projDoc);
	}

	private void exportCategories(Set<Category> categories, ProjectType catParentDoc) {
		for (Category category : categories) {
			CategoryType catDoc = catParentDoc.addNewCategoryChild();
			exportCategory(category, catDoc);
		}
	}

	private void exportCategories(Set<Category> categories, CategoryType catParentDoc) {
		for (Category category : categories) {
			CategoryType catDoc = catParentDoc.addNewCategoryChild();
			exportCategory(category, catDoc);
		}
	}

	private void exportCategory(Category category, CategoryType catDoc) {
		catDoc.setName(str(category.getName()));
		catDoc.setDescription(str(category.getDescription()));
		for (PropertySet propertySet : category.getPropertySets()) {
			RefType propSetRef = catDoc.addNewPropertySet();
			propSetRef.setRef(getPropSetRef(propertySet));
		}
		for (Requirement req : category.getRequirements()) {
			RequirementType reqDoc = catDoc.addNewRequirement();
			exportRequirement(req, reqDoc);
			requirements.put(req.getDbId(),reqDoc);
		}
		exportCategories(category.getSubCategories(), catDoc);
	}

	private void exportRequirement(Requirement req, RequirementType reqDoc) {
		Long businessID = req.getDbId();
		reqDoc.setRequirementId(businessID);
		reqDoc.setName(str(req.getName()));
		reqDoc.setDescription(str(req.getDescription()));
		for (Stakeholder stakeholder : req.getStakeholders()) {
			RefType ref = reqDoc.addNewStakeholder();
			ref.setRef(getStakeholderRef(stakeholder));
		}
		for (PropertyValue propertyValue : req.getPropertyValues()) {
			PropertyValueType propValDoc = reqDoc.addNewPropertyValue();
			RefType propDef = propValDoc.addNewPropertyDef();
			propDef.setRef(getPropDefRef(propertyValue.getPropertyDef()));
			propValDoc.setValue(str(propertyValue.getValue()));
		}
		RefType assignedTo = reqDoc.addNewAssignedTo();
//		assignedTo.setRef(getStakeholderRef(req.getAssignedTo()));
//		RefType reporterOrOwner = reqDoc.addNewReporterOrOwner();
//		reporterOrOwner.setRef(getStakeholderRef(req.getReporterOrOwner()));
		reqDoc.setPriority(req.getPriority());
//		reqDoc.setStatus(req.getStatus());
//		reqDoc.setVersion(req.getVersion());
//		reqDoc.setLastUpdatedDate(new XmlCalendar(req.getLastUpdatedDate()));
//		reqDoc.setSubmittedDate(new XmlCalendar(req.getSubmittedDate()));
	}

	private String getPropDefRef(PropertyDef propertyDef) {
		return str(propertyDef.getName());
	}

	private String getPropSetRef(PropertySet propertySet) {
		return str(propertySet.getName());
	}

	private String getStakeholderRef(Stakeholder stakeholder) {
		return (null == stakeholder) ? "" : str(stakeholder.getName());
	}

	private void setupLinkedRequirements(RequirementSystem requirementSystem, com.wgo.precise.server.preciseData.RequirementSystemDocument.RequirementSystem rsDoc) {
		for (Project project : requirementSystem.getProjects()) {
			for (Category category : project.getCategories()) {
				for (Requirement req : category.getRequirements()) {
					RequirementType reqDoc = requirements.get(req.getDbId());
					for (Requirement linkedReq : req.getLinkedRequirements()) {
						RefType ref = reqDoc.addNewLinkedRequirement();
						ref.setRef(""+linkedReq.getDbId());
					}
				}
			}
		}
	}

	
}
